<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>设计模式——观察者模式</title>
    <script type="application/javascript" charset="UTF-8" src="../../common/CommonUtils.js"></script>
    <script type="application/javascript" charset="UTF-8">
        // 观察者模式 ： 对程序中的某一对象进行实时的观察，当该对象的状态发生改变时，进行通知
        // 可以分为两个角色 ： 观察者[订阅者]  被观察者[发布者]
        // 一个经典案例 ： 读者订阅报纸  报社 => 被观察者[发布者]  读者 => 观察者[订阅者]

        // 报社 ： 被观察者[发布者]
        var Publish = function (name) {
            // 报社的名字
            this.name = name;
            // 存放订阅该报社的读者, 需要注意的是，每一个元素都是函数类型
            this.subscribers = [];
        };

        // 报社发布消息的方法
        Publish.prototype.deliver = function (news) {
            var publish = this; // 保存Publish实例对象的引用
            // 循环遍历该报社的所有订阅者并发布消息
            this.subscribers.forEach(function (fn) {
                fn(news, publish);
            });
            return this;  // 链式编程
        };

        // 具体的订阅者应该有一个订阅报纸的方法
        // 因为订阅者都是函数类型，所有就将方法加入到Function的原型对象上
        Function.prototype.subscribe = function (publish) {
            var sub = this;  // 保存订阅者的引用
            // 判断订阅报社的订阅者数组，如果没有该订阅者的时候，将该订阅者加入到 subscribers
            var alreadyExists = publish.subscribers.some(function (item) {
                return item === sub;
            });
            // 报社的subscribers中不存在该订阅者时
            if ( ! alreadyExists) {
                publish.subscribers.push(this);
            }
            return this;  // 链式编程
        };

        // 具体的订阅者还要有一个取消订阅的方法
        Function.prototype.unSubscribe = function (publish) {
            var sub = this;  // 保存订阅者的引用
            // 利用filter函数，在报社的subscribers中过滤不是该报社的所有订阅者，
            // 即从subscribers删除该订阅者，也就是完成了取消订阅的操作
            publish.subscribers = publish.subscribers.filter(function (item) {
                return item !== sub;
            });
            return this;  // 链式编程
        };

        window.onload = function(){

            //实例化发布者对象(报社对象、被观察者)
            var pub1 = new Publish('第一报社');
            var pub2 = new Publish('第二报社');
            var pub3 = new Publish('第三报社');

            //观察者(订阅者)
            var sub1 = function(news){
                document.getElementById('sub1').innerHTML += arguments[1].name + ':' + news + '\n';
            };
            var sub2 = function(news){
                document.getElementById('sub2').innerHTML += arguments[1].name + ':' + news + '\n';
            };

            //执行订阅方法
            sub1.subscribe(pub1).subscribe(pub2).subscribe(pub3);
            sub2.subscribe(pub1).subscribe(pub2);

            //事件绑定
            WL.EventUtil.addHandler(document.getElementById('pub1'),'click' , function(){
                pub1.deliver(document.getElementById('text1').value);
            });
            WL.EventUtil.addHandler(document.getElementById('pub2'),'click' , function(){
                pub2.deliver(document.getElementById('text2').value);
            });
            WL.EventUtil.addHandler(document.getElementById('pub3'),'click' , function(){
                pub3.deliver(document.getElementById('text3').value);
            });
            sub1.unSubscribe(pub1); //取消订阅
        };
    </script>
</head>
<body>
    <input id="pub1" type="button" value="第一报社" /><input id="text1" value="" /><br>
    <input id="pub2" type="button" value="第二报社" /><input id="text2" value="" /><br>
    <input id="pub3" type="button" value="第三报社" /><input id="text3" value="" /><br>
    
    <textarea id="sub1" rows="5" cols="30" ></textarea>
    <textarea id="sub2" rows="5" cols="30" ></textarea>
</body>
</html>